package com.uinnova.product.eam.service.fx.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.db.bean.DiagramChangeData;
import com.uinnova.product.eam.feign.workable.FlowableFeign;
import com.uinnova.product.eam.model.ArtifactConstraintVo;
import com.uinnova.product.eam.model.EamArtifactElementVo;
import com.uinnova.product.eam.model.bm.DiagramPrivateAndDesginData;
import com.uinnova.product.eam.model.vo.CheckAssertEditCiParam;
import com.uinnova.product.eam.model.vo.CheckAssertEditReq;
import com.uinnova.product.eam.model.vo.CheckAssertEditRes;
import com.uinnova.product.eam.service.BmDiagramSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamArtifactColumnSvc;
import com.uinnova.product.eam.service.IEamArtifactSvc;
import com.uinnova.product.eam.service.asset.AppSystemSvc;
import com.uinnova.product.eam.service.bm.impl.FlowModelMergePreProcessor;
import com.uinnova.product.eam.service.es.IamsESCIPrivateSvc;
import com.uinnova.product.eam.service.es.IamsESCIRltPirvateSvc;
import com.uinnova.product.eam.service.fx.ProcessCiRltSvc;
import com.uinnova.product.eam.service.fx.ProcessDiagramSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltDesignSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltPrivateSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.eam.service.utils.DataModelDiagramUtil;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagramDTO;
import com.uinnova.project.base.diagram.comm.model.ESDiagramInfoDTO;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uino.api.client.cmdb.ICIClassApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.util.sys.CheckAttrUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProcessCiRltSvcImpl implements ProcessCiRltSvc {

    @Autowired
    ProcessDiagramSvc processDiagramSvc;

    @Autowired
    IUserApiSvc userApiSvc;

    @Autowired
    ICISwitchSvc ciSwitchSvc;

    @Autowired
    IamsCIRltSwitchSvc ciRltSwitchSvc;

    @Autowired
    IamsESCIPrivateSvc iamsESCIPrivateSvc;

    @Autowired
    IamsCIRltPrivateSvc iamsCIRltPrivateSvc;

    @Autowired
    IamsCIRltDesignSvc iamsCIRltDesignSvc;

    @Autowired
    IamsESCIRltPirvateSvc esCIRltPirvateSvc;

    @Autowired
    IEamArtifactColumnSvc iEamArtifactColumnSvc;

    @Autowired
    ICIClassApiSvc iciClassApiSvc;
    @Autowired
    private FlowModelMergePreProcessor flowModelMergePreProcessor;

    @Autowired
    IEamArtifactSvc iEamArtifactSvc;

    @Autowired
    ESDiagramApiClient diagramApiClient;

    @Autowired
    FlowableFeign flowableFeign;

    @Autowired
    AppSystemSvc appSystemSvc;
    @Autowired
    private BmDiagramSvc bmDiagramSvc;

    @Override
    public SysUser getUserInfoByOwnerCode(String ownerCode) {
        // 当前用户信息
        SysUser currentUserInfo = new SysUser();
        if (BinaryUtils.isEmpty(ownerCode)) {
            currentUserInfo = SysUtil.getCurrentUserInfo();
        } else {
            CSysUser cSysUser = new CSysUser();
            cSysUser.setLoginCodeEqual(ownerCode);
            List<SysUser> sysUserInfo = userApiSvc.getSysUserByCdt(cSysUser);
            if (CollectionUtils.isEmpty(sysUserInfo)) {
                throw new BinaryException("当前视图所属用户不存在!");
            }
            currentUserInfo = sysUserInfo.get(0);
        }
        return currentUserInfo;
    }

    @Override
    public DiagramPrivateAndDesginData getPrivateAndDesginDataByDEnergyId(List<ESDiagramDTO> esDiagramDTOS, SysUser currentUserInfo) {
        return this.getPrivateAndDesginDataByDEnergyId(esDiagramDTOS, currentUserInfo, false);
    }

    @Override
    public DiagramPrivateAndDesginData getPrivateAndDesginDataByDEnergyId(List<ESDiagramDTO> esDiagramDTOS, SysUser currentUserInfo, Boolean isCustom) {
        // 根据传参DTO 获取视图数据
        List<ESDiagramInfoDTO> diagramInfoList = new ArrayList<>();
        if(BinaryUtils.isEmpty(esDiagramDTOS)){
            return null;
        }
        esDiagramDTOS.forEach(e -> {
            diagramInfoList.add(e.getDiagram());
        });

        // 查询视图上的 CI / RLT 数据 (私有库)
        Set<String> privateCICodes = DataModelDiagramUtil.getDiagramCiList(diagramInfoList);
        Set<String> privateRltCodes = DataModelDiagramUtil.getDiagramRltList(diagramInfoList);
        //添加对ER图的特殊关系处理
        privateRltCodes.addAll(flowModelMergePreProcessor.queryRltCode(esDiagramDTOS));

        // 根据当前code查询出 私有库 / 设计库 对应数据
        List<CcCiInfo> privateCiInfos = new ArrayList<>();
        List<CcCiInfo> desginCiInfos = new ArrayList<>();
        List<ESCIRltInfo> desginRltInfos = new ArrayList<>();
        List<ESCIRltInfo> privateRltInfos = new ArrayList<>();

        if (!BinaryUtils.isEmpty(privateCICodes)) {
            CCcCi cdt = new CCcCi();
            cdt.setCiCodes(privateCICodes.toArray(new String[privateCICodes.size()]));
            desginCiInfos = ciSwitchSvc.queryCiInfoList(currentUserInfo.getDomainId(), cdt, null, Boolean.FALSE, Boolean.TRUE, LibType.DESIGN);
            // 设置ownerCode 查询条件 查询私有库数据
            cdt.setOwnerCodeEqual(currentUserInfo.getLoginCode());
            privateCiInfos = ciSwitchSvc.queryCiInfoList(currentUserInfo.getDomainId(), cdt, null, Boolean.FALSE, Boolean.TRUE, LibType.PRIVATE);
        }
        if (!BinaryUtils.isEmpty(privateRltCodes)) {
            ESRltSearchBean esRltSearchBean = new ESRltSearchBean();
            esRltSearchBean.setPageNum(1);
            esRltSearchBean.setPageSize(1000);
            esRltSearchBean.setRltUniqueCodes(privateRltCodes);
            esRltSearchBean.setDomainId(currentUserInfo.getDomainId());
            Page<ESCIRltInfo> desginciRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.DESIGN);
            desginRltInfos = desginciRltInfoPage.getData();
            esRltSearchBean.setOwnerCode(currentUserInfo.getLoginCode());
            Page<ESCIRltInfo> privateRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.PRIVATE);
            privateRltInfos = privateRltInfoPage.getData();
        }
//        if (!isCustom) {
//            // 过滤应用系统相关CI/RLT数据
//            String appSystemClassCode = Env.HT_APP_SYSTEM.getClassCode();
//            ESCIClassInfo appSystemClassInfo = appSystemSvc.getAppSystemClassInfo();
//            Long appSystemClassId = appSystemClassInfo.getId();
//            privateCiInfos = privateCiInfos.stream().filter(pci -> !pci.getCiClass().getClassCode().equals(appSystemClassCode)).collect(Collectors.toList());
//            desginCiInfos = desginCiInfos.stream().filter(dci -> !dci.getCiClass().getClassCode().equals(appSystemClassCode)).collect(Collectors.toList());
//            privateRltInfos = privateRltInfos.stream().filter(prlt -> !Objects.equals(appSystemClassId.longValue(), prlt.getSourceClassId().longValue()) &&
//                    !Objects.equals(appSystemClassId.longValue(), prlt.getTargetClassId().longValue())).collect(Collectors.toList());
//            desginRltInfos = desginRltInfos.stream().filter(drlt -> !Objects.equals(appSystemClassId.longValue(), drlt.getSourceClassId().longValue()) &&
//                    !Objects.equals(appSystemClassId.longValue(), drlt.getTargetClassId().longValue())).collect(Collectors.toList());
//        }

        // 返回拼装数据  私有库 / 设计库 CI RLT 数据
        DiagramPrivateAndDesginData data = new DiagramPrivateAndDesginData();
        data.setPrivateCiInfos(privateCiInfos);
        data.setDesginCiInfos(desginCiInfos);
        data.setPrivateRltInfos(privateRltInfos);
        data.setDesginRltInfos(desginRltInfos);
        log.info("##########私有库CI数量:【{}】,设计库CI数量:【{}】,私有库RLT数量:【{}】,设计库RLT数量:【{}】",
                privateCiInfos.size(), desginCiInfos.size(), privateRltInfos.size(), desginRltInfos.size());
        return data;
    }

    @Override
    public DiagramPrivateAndDesginData getAndDealPrivateAndDesginDataByDEnergyId(List<ESDiagramDTO> esDiagramDTOS, SysUser currentUserInfo) {
        // 根据传参DTO 获取视图数据
        List<ESDiagramInfoDTO> diagramInfoList = new ArrayList<>();
        if(BinaryUtils.isEmpty(esDiagramDTOS)){
            return null;
        }
        esDiagramDTOS.forEach(e -> {
            diagramInfoList.add(e.getDiagram());
        });

        // 查询视图上的 CI / RLT 数据 (私有库)
        Set<String> privateCICodes = DataModelDiagramUtil.getDiagramCiList(diagramInfoList);
        Set<String> privateRltCodes = DataModelDiagramUtil.getDiagramRltList(diagramInfoList);
        //添加对ER图的特殊关系处理
        privateRltCodes.addAll(flowModelMergePreProcessor.queryRltCode(esDiagramDTOS));

        // 根据当前code查询出 私有库 / 设计库 对应数据
        List<CcCiInfo> privateCiInfos = new ArrayList<>();
        List<CcCiInfo> desginCiInfos = new ArrayList<>();
        List<ESCIRltInfo> desginRltInfos = new ArrayList<>();
        List<ESCIRltInfo> privateRltInfos = new ArrayList<>();

        if (!BinaryUtils.isEmpty(privateCICodes)) {
            privateCiInfos = dealCiCaseUnSameKey(privateCICodes, currentUserInfo.getLoginCode());
            Set<String> finalPrivateCiCodes = privateCiInfos.stream().map(e -> e.getCi().getCiCode()).collect(Collectors.toSet());
            CCcCi cdt = new CCcCi();
            cdt.setCiCodes(finalPrivateCiCodes.toArray(new String[finalPrivateCiCodes.size()]));
            desginCiInfos = ciSwitchSvc.queryCiInfoList(currentUserInfo.getDomainId(), cdt, null, Boolean.FALSE, Boolean.TRUE, LibType.DESIGN);
        }
        if (!BinaryUtils.isEmpty(privateRltCodes)) {
            ESRltSearchBean esRltSearchBean = new ESRltSearchBean();
            esRltSearchBean.setPageNum(1);
            esRltSearchBean.setPageSize(1000);
            esRltSearchBean.setRltUniqueCodes(privateRltCodes);
            esRltSearchBean.setDomainId(currentUserInfo.getDomainId());
            Page<ESCIRltInfo> desginciRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.DESIGN);
            desginRltInfos = desginciRltInfoPage.getData();
            esRltSearchBean.setOwnerCode(currentUserInfo.getLoginCode());
            Page<ESCIRltInfo> privateRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.PRIVATE);
            privateRltInfos = privateRltInfoPage.getData();
        }
        // 返回拼装数据  私有库 / 设计库 CI RLT 数据
        DiagramPrivateAndDesginData data = new DiagramPrivateAndDesginData();
        data.setPrivateCiInfos(privateCiInfos);
        data.setDesginCiInfos(desginCiInfos);
        data.setPrivateRltInfos(privateRltInfos);
        data.setDesginRltInfos(desginRltInfos);
        log.info("##########私有库CI数量:【{}】,设计库CI数量:【{}】,私有库RLT数量:【{}】,设计库RLT数量:【{}】",
                privateCiInfos.size(), desginCiInfos.size(), privateRltInfos.size(), desginRltInfos.size());
        return data;
    }

    private List<CcCiInfo> dealCiCaseUnSameKey(Set<String> privateCICodes, String diagramCreator) {
        if (CollectionUtils.isEmpty(privateCICodes)) {
            return new ArrayList<>();
        }
        CCcCi cdt = new CCcCi();
        cdt.setCiCodes(privateCICodes.toArray(new String[privateCICodes.size()]));
        // 设置ownerCode 查询条件 查询私有库数据
        cdt.setOwnerCodeEqual(diagramCreator);
        List<CcCiInfo> privateCiInfos = ciSwitchSvc.queryCiInfoList(1L, cdt, null, Boolean.FALSE, Boolean.TRUE, LibType.PRIVATE);
        if (CollectionUtils.isEmpty(privateCiInfos)) {
            return privateCiInfos;
        }
        List<CheckAssertEditCiParam> ciParams = new ArrayList<>();
        for (CcCiInfo ciInfo : privateCiInfos) {
            CheckAssertEditCiParam ciParam = new CheckAssertEditCiParam();
            ciParam.setCiCode(ciInfo.getCi().getCiCode());
            ciParam.setCiPrimaryKey(ciInfo.getCi().getCiPrimaryKey());
            ciParam.setLibType(LibType.PRIVATE);
            ciParams.add(ciParam);
        }
        CheckAssertEditReq req = new CheckAssertEditReq();
        req.setFromDiagram(true);
        req.setOperator(diagramCreator);
        req.setCiParams(ciParams);
        List<CheckAssertEditRes> assertEditRes = bmDiagramSvc.checkAssertEditAuth(req, false);
        if (CollectionUtils.isEmpty(assertEditRes)) {
            return privateCiInfos;
        }
        Map<String, ESCIInfo> sameKeyDesignCiInfoMap = new ConcurrentHashMap<>();
        for (CheckAssertEditRes res : assertEditRes) {
            if (res.getEdit() || res.getSameKeyDesignCiInfo() == null) {
                continue;
            }
            sameKeyDesignCiInfoMap.put(res.getCiCode(), res.getSameKeyDesignCiInfo());
            sameKeyDesignCiInfoMap.put(res.getCiPrimaryKey(), res.getSameKeyDesignCiInfo());
        }
        if (org.springframework.util.CollectionUtils.isEmpty(sameKeyDesignCiInfoMap)) {
            return privateCiInfos;
        }
        Map<Long, String> replaceMap = new HashMap<>();
        List<ESCIInfo> updateInfo = new ArrayList<>();
        Set<Long> updateClassIds = new HashSet<>();
        Map<String, ESCIInfo> freshCodeMap = new HashMap<>();
        Map<String, String> designSamekeyCIOwner = new ConcurrentHashMap<>();
        for (CcCiInfo privateInfo : privateCiInfos) {
            ESCIInfo updateCiInfo = EamUtil.tranESCIInfo(privateInfo);
            String ciCode = updateCiInfo.getCiCode();
            String ciPrimaryKey = updateCiInfo.getCiPrimaryKey();
            //ci不一致、主键不一致、有操作权限
            if (!sameKeyDesignCiInfoMap.containsKey(ciCode)
                    && !sameKeyDesignCiInfoMap.containsKey(ciPrimaryKey)) {
                continue;
            }
            ESCIInfo desginInfo = sameKeyDesignCiInfoMap.get(ciPrimaryKey);
            //ci不一致、主键一致时需要刷一下ciCode
            boolean refreshCiCode = !sameKeyDesignCiInfoMap.containsKey(ciCode)
                    && sameKeyDesignCiInfoMap.containsKey(ciPrimaryKey);
            if (refreshCiCode) {
                replaceMap.put(updateCiInfo.getId(), desginInfo.getCiCode());
            }
            designSamekeyCIOwner.put(desginInfo.getCiCode(), desginInfo.getOwnerCode());
            freshCodeMap.put(ciCode, desginInfo);
            //私有库数据强制修改为设计库数据 发布版本修正为设计库版本 本地版本归0
            updateCiInfo.setAttrs(desginInfo.getAttrs());
            updateCiInfo.setCiCode(desginInfo.getCiCode());
            updateCiInfo.setPublicVersion(desginInfo.getPublicVersion());
            updateCiInfo.setLocalVersion(0L);
            updateClassIds.add(updateCiInfo.getClassId());
            updateInfo.add(updateCiInfo);
        }
        if (!org.springframework.util.CollectionUtils.isEmpty(freshCodeMap)) {
            iamsESCIPrivateSvc.replaceCiCodeByIds(replaceMap);
        }
        if (!org.springframework.util.CollectionUtils.isEmpty(updateInfo)) {
            ciSwitchSvc.saveOrUpdateBatchCI(updateInfo, new ArrayList<>(updateClassIds),
                    diagramCreator, diagramCreator, LibType.PRIVATE);
            processDiagramSvc.freshDiagramNode(freshCodeMap, diagramCreator);
        }

        List<CcCiInfo> ciInfos = ciSwitchSvc.queryCiInfoList(1L, cdt, null, Boolean.FALSE, Boolean.TRUE, LibType.PRIVATE);
        if (CollectionUtils.isEmpty(ciInfos) || org.springframework.util.CollectionUtils.isEmpty(designSamekeyCIOwner)) {
            return ciInfos;
        }
        for (CcCiInfo ciInfo : ciInfos) {
            if (!designSamekeyCIOwner.containsKey(ciInfo.getCi().getCiCode())) {
                continue;
            }
            ciInfo.getCi().setOwnerCode(designSamekeyCIOwner.get(ciInfo.getCi().getCiCode()));
        }
        return ciInfos;
    }

    @Override
    public Map<String, List<DiagramChangeData>> getChangeCIDataByDEnergyIds(DiagramPrivateAndDesginData privateAndDesginDataByDEnergyId) {
        /*
         *  根据视图内的CI/RLT信息生成视图的变更及数据 （RLT数据暂时不考虑，直接去覆盖或新增到设计库）
         *  当前视图数据对比设计库分为三类操作数据 新增（ADD） 变更（UPD） 不变（NO）{后期考虑是否添加删除类型数据（DEL）}
         *  返回给上层服务当前视图的所有区别数据 根据上层服务特有的发布逻辑对视图进行处理 确认最终发布视图
         *  返回的数据格式
         *  补充：批量发布 CI/RLT都是以用户为维度创建，所以多张视图可以将结果集融合处理，避免单张处理CI的publishVersion字段反复更新
         *
         *  ADD
         *      CI (私有库， 设计库) ----新增返回数据设计库为空
         *  UPD
         *      CI (私有库， 设计库)
         *  NO
         *      CI (私有库， 设计库)
         *
         * */

        // 返回数据 变更数据集 （ADD UPD NO）
        Map<String, List<DiagramChangeData>> finalChangeData = new HashMap<>();

        // 此时读取出的数据要保证所有设计库存在的数据都在私有库进行了绑定
        List<CcCiInfo> privateCiInfos = privateAndDesginDataByDEnergyId.getPrivateCiInfos();
        List<CcCiInfo> desginCiInfos = privateAndDesginDataByDEnergyId.getDesginCiInfos();

        List<DiagramChangeData> addData = new ArrayList<>();
        List<DiagramChangeData> updData = new ArrayList<>();
        List<DiagramChangeData> noData = new ArrayList<>();
        // 提取视图中新增的 CI RLT
        if (CollectionUtils.isEmpty(desginCiInfos)) {
            // 当前私有库 CI 都为新增 遍历私有库数据放入返回对象
            for (CcCiInfo privateCI : privateCiInfos) {
                DiagramChangeData diagramChangeData = new DiagramChangeData(privateCI, null, null, null);
                addData.add(diagramChangeData);
            }
            finalChangeData.put("ADD", addData);
            finalChangeData.put("UPD", updData);
            finalChangeData.put("NO", noData);
            return finalChangeData;
        }

        Map<String, CcCiInfo> desginCICodeAndDatas = new HashMap<>();
        for (CcCiInfo desginCI : desginCiInfos) {
            desginCICodeAndDatas.put(desginCI.getCi().getCiCode(), desginCI);
        }
        List<String> filterData = new ArrayList<>();        // 这个是私有库新增的数据code集合
        if (privateCiInfos.size() > desginCiInfos.size()) {
            // 设计库数据不为空 当设计库与私有库数量不一致时 单独筛选出ADD数据
            for (CcCiInfo privateCI : privateCiInfos) {
                if (!desginCICodeAndDatas.containsKey(privateCI.getCi().getCiCode())) {
                    DiagramChangeData diagramChangeData = new DiagramChangeData(privateCI, null, null, null);
                    addData.add(diagramChangeData);
                    filterData.add(privateCI.getCi().getCiCode());
                }
            }
        }

        /*
         *  循环遍历 私有库 / 设计库 数据 根据私有库的localVersion与publishVersion 和 设计库的 publishVersion 字段判断当前CI的 UPD NO 状态
         *  UPD: 设计库的publishVersion = 私有库的publishVersion localVersion>0时 数据为更新  (当前publishVersion逻辑上已经在发布之前的版本校验与设计库形成统一，设计库与私有库相同)
         *  NO: 设计库的publishVersion = 私有库的publishVersion localVersion=0时 数据为不变
         * */
        for (CcCiInfo privateCI : privateCiInfos) {
            if (!filterData.contains(privateCI.getCi().getCiCode())) {
                // 这部分判断localVersion != 0 原因 新建的CI LV默认会=1 这部分数据已经在上面处理完成 不影响 ，修改的时候自增+1 但是 公共保存CI方法/检出/从画布拖拽设计库对象 时设置LV=0
                if (privateCI.getCi().getLocalVersion() != 0 &&
                        privateCI.getCi().getPublicVersion() >= desginCICodeAndDatas.get(privateCI.getCi().getCiCode()).getCi().getPublicVersion()) {
                    // 修改过的数据 标记为 UPD
                    DiagramChangeData diagramChangeData = new DiagramChangeData(privateCI, desginCICodeAndDatas.get(privateCI.getCi().getCiCode()), null, null);
                    updData.add(diagramChangeData);
                } else {
                    // 未修改的数据 标记为 NO 也可能是版本比较低的数据
                    DiagramChangeData diagramChangeData = new DiagramChangeData(privateCI, desginCICodeAndDatas.get(privateCI.getCi().getCiCode()), null, null);
                    noData.add(diagramChangeData);
                }
            }
        }
        finalChangeData.put("ADD", addData);
        finalChangeData.put("NO", noData);
        finalChangeData.put("UPD", updData);
        log.info("##########变更的CI数据数量：ADD:【{}】,UPD:【{}】,NO:【{}】",
                finalChangeData.get("ADD").size(), finalChangeData.get("UPD").size(), finalChangeData.get("NO").size());
        return finalChangeData;
    }

    @Override
    public List<CcCiInfo> dealPublishDiagramCI(Map<String, List<DiagramChangeData>> changeCIDataByDEnergyId, String loginCode) {
        return this.dealHTPublishDiagramCI(changeCIDataByDEnergyId, loginCode, true);
    }

    @Override
    public List<CcCiInfo> dealHTPublishDiagramCI(Map<String, List<DiagramChangeData>> changeCIDataByDEnergyId, String loginCode, Boolean isCustom) {
        /*
         *  发布数据处理：
         *       私有库：localVersion归零 publishVersion + 1 （当前publishVersion + 1不针对无修改的数据）
         *       设计库：publishVersion + 1
         * */

        /*
         *  华泰定制需求：发布视图 需要对画布上的应用系统数据进行处理 本地修改后不更新到设计库
         * */

        // 华泰应用系统标识
        String htAppSystemCode = Env.HT_APP_SYSTEM.getClassCode();

        // 获取视图上的 CI 变更数据集
        List<CcCiInfo> ciDataList = new ArrayList<>();
        List<DiagramChangeData> addDataList = changeCIDataByDEnergyId.get("ADD");
        List<DiagramChangeData> updDataList = changeCIDataByDEnergyId.get("UPD");
        List<DiagramChangeData> noDataList = changeCIDataByDEnergyId.get("NO");
        if (CollectionUtils.isEmpty(addDataList) && CollectionUtils.isEmpty(updDataList) && CollectionUtils.isEmpty(noDataList)) {
            return new ArrayList<>();
        }

        // 私有库需要处理的CI数据code集合  最后统一将私有库的publicVersion + 1 防止再次发布时版本冲突 （这部分数据排除了没有修改的CI）
        List<String> privateFreshCodes = new ArrayList<>();
        // 根据所有的私有库ciCode去设计库查询CI 最后需要返回继续做给处理关系的接口作为参数
        List<String> allPrivateCode = new ArrayList<>();
        List<Long> classIds = new ArrayList<>();

        if (!CollectionUtils.isEmpty(addDataList)) {
            for (DiagramChangeData diagramChangeData : addDataList) {
                CcCiInfo privateCiInfo = diagramChangeData.getPrivateCiInfo();
                privateCiInfo.getCi().setPublicVersion(privateCiInfo.getCi().getPublicVersion() + 1);
                privateCiInfo.getCi().setId(null);
                ciDataList.add(privateCiInfo);
                privateFreshCodes.add(privateCiInfo.getCi().getCiCode());
                classIds.add(privateCiInfo.getCi().getClassId());
                allPrivateCode.add(privateCiInfo.getCi().getCiCode());
            }
        }
        if (!CollectionUtils.isEmpty(updDataList)) {
            for (DiagramChangeData diagramChangeData : updDataList) {
                CcCiInfo privateCiInfo = diagramChangeData.getPrivateCiInfo();
                privateCiInfo.getCi().setPublicVersion(privateCiInfo.getCi().getPublicVersion() + 1);
                privateCiInfo.getCi().setId(null);
                ciDataList.add(privateCiInfo);
                privateFreshCodes.add(privateCiInfo.getCi().getCiCode());
                classIds.add(privateCiInfo.getCi().getClassId());
                allPrivateCode.add(privateCiInfo.getCi().getCiCode());
            }
        }
        // 这里可能存在 当发布版本落后于设计库 本地版本为初始值时 需要强制更新CI数据
        if (!CollectionUtils.isEmpty(noDataList)) {
            for (DiagramChangeData diagramChangeData : noDataList) {
                CcCiInfo privateCiInfo = diagramChangeData.getPrivateCiInfo();
                // 私有库publishVersion < 设计库publishVersion
                if (diagramChangeData.getDesginCiInfo().getCi().getPublicVersion() > privateCiInfo.getCi().getPublicVersion()) {
                    // 私有库publicVersion升级
                    privateCiInfo.getCi().setPublicVersion(diagramChangeData.getDesginCiInfo().getCi().getPublicVersion() + 1);
                    // 这里必须先把私有库的publicVersion升级为设计库 后续需要调接口统一将版本号升级
                    iamsESCIPrivateSvc.upgradeVersionNo(privateCiInfo.getCi().getId(), diagramChangeData.getDesginCiInfo().getCi().getPublicVersion());
                    privateCiInfo.getCi().setId(null);
                    ciDataList.add(privateCiInfo);
                    privateFreshCodes.add(privateCiInfo.getCi().getCiCode());       // 在发布视图之前统一升级CI版本号
                    classIds.add(privateCiInfo.getCi().getClassId());
                }
                allPrivateCode.add(privateCiInfo.getCi().getCiCode());
            }
        }
        // 更新私有库localversion为0， publicVersion+1
        iamsESCIPrivateSvc.increaseCiPublicVersionByCiCodes(privateFreshCodes, loginCode);

        // 批量更新设计库数据
        List<ESCIInfo> esciInfos = EamUtil.coverCiInfoList(ciDataList);
        if (!CollectionUtils.isEmpty(esciInfos)) {
            ciSwitchSvc.saveOrUpdateBatchCI(esciInfos, classIds, loginCode, loginCode, LibType.DESIGN);
        }

//        if (!BinaryUtils.isEmpty(noDataList)) {
//            for (DiagramChangeData diagramChangeData : noDataList) {
//                privateFreshCodes.add(diagramChangeData.getDesginCiInfo().getCi().getCiCode());
//            }
//        }

        // 这里要查询所有数据 返回给视图发布绑定CI版本号
        List<CcCiInfo> ccCiInfos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(allPrivateCode)) {
            CCcCi cdt = new CCcCi();
            cdt.setCiCodes(allPrivateCode.toArray(new String[allPrivateCode.size()]));
            ccCiInfos = ciSwitchSvc.queryCiInfoList(1L, cdt, null, false, false, LibType.DESIGN);
        }
        return ccCiInfos;
    }

    @Override
    public List<ESCIRltInfo> dealPublishDiagramRlt(DiagramPrivateAndDesginData privateAndDesginDataByDEnergyId, List<CcCiInfo> ccCiInfos) {
        Map<String, CcCiInfo> ciCodeAndDataMap = new HashMap<>();
        for (CcCiInfo ccCiInfo : ccCiInfos) {
            ciCodeAndDataMap.put(ccCiInfo.getCi().getCiCode(), ccCiInfo);
        }
        // 获取视图上的 RLT 数据
        List<ESCIRltInfo> privateRltInfos = privateAndDesginDataByDEnergyId.getPrivateRltInfos();
        List<ESCIRltInfo> desginRltInfos = privateAndDesginDataByDEnergyId.getDesginRltInfos();

        Map<String, ESCIRltInfo> desginRltCodeAndDataMap = new HashMap<>();
        for (ESCIRltInfo desginRlt : desginRltInfos) {
            desginRltCodeAndDataMap.put(desginRlt.getCiCode(), desginRlt);
        }

        Set<String> rltCode = new HashSet<>();
        Set<BindCiRltRequestDto> bindCiRltRequestDtos = new HashSet<>();
        List<ESCIRltInfo> updateVersionList = new ArrayList<>();
        Map<Long, Map<String, String>> updateCiRltAttrMap = new HashMap<>();
        for (ESCIRltInfo privateRlt : privateRltInfos) {
            if (desginRltCodeAndDataMap.containsKey(privateRlt.getCiCode())) {
                // 更新处理
                ESCIRltInfo desginRlt = desginRltCodeAndDataMap.get(privateRlt.getCiCode());
                updateCiRltAttrMap.put(desginRlt.getId(), privateRlt.getAttrs());
                //iamsCIRltDesignSvc.updateCiRltAttr(desginRlt.getId(), privateRlt.getAttrs());
                rltCode.add(desginRlt.getCiCode());
                // 这里还需要兼容一下之前的视图 已经创建好关系 没有localVersion字段
                if (BinaryUtils.isEmpty(privateRlt.getLocalVersion())) {
                    // 没有localVersion字段 手动比较attrs的值 判断是否修改
//                    if (!CheckAttrUtil.checkAttrMapEqual(privateRlt.getAttrs(), desginRltCodeAndDataMap.get(privateRlt).getAttrs())) {
//                        privateRlt.setPublicVersion(BinaryUtils.isEmpty(privateRlt.getPublicVersion()) ? 1 : privateRlt.getPublicVersion() + 1);
//                        privateRlt.setLocalVersion(1L);
//                    } else {
//                        // 没有修改过的话
//                        privateRlt.setPublicVersion(1L);
//                        privateRlt.setLocalVersion(1L);
//                    }
                    /*
                     *  修改过attrs
                     *     将本地数据的localVersion置1 publicversion加1（这种数据的publicversion没有值， 下面的操作需要兼容）
                     *     设计库的关系数据从当前时刻开始生成历史数据 之前的没有记录
                     *  没有修改过attrs
                     *     给publicversion理论上设置为1，但是存在其他用户发布的可能，自增加1  将localversion设置为1即可
                     *  所以这里的数据实际上不需要检验修改 可以合并处理
                     *
                     * */
                    privateRlt.setPublicVersion(BinaryUtils.isEmpty(desginRltCodeAndDataMap.get(privateRlt.getCiCode()).getPublicVersion()) ?
                            1 : desginRltCodeAndDataMap.get(privateRlt.getCiCode()).getPublicVersion() + 1);
                    privateRlt.setLocalVersion(1L);
                    updateVersionList.add(privateRlt);
                } else {
                    // 判断私有库localVersion是否增加 去做后续更新处理
                    // 私有库的关系数据 版本号应该统一成 设计库关系数据当前版本号 这里直接+1（不正确） 如果需要加关系冲突校验会有问题 desginRltCodeAndData 的数据版本号需要在发布之前确认为最新的
                    if (privateRlt.getLocalVersion() > 1) {  // 私有库更新过
                        privateRlt.setPublicVersion(BinaryUtils.isEmpty(desginRltCodeAndDataMap.get(privateRlt.getCiCode()).getPublicVersion()) ?
                                1 : desginRltCodeAndDataMap.get(privateRlt.getCiCode()).getPublicVersion() + 1);
                        privateRlt.setLocalVersion(1L);
                        updateVersionList.add(privateRlt);
                    }
                }
            } else {
                // 新建处理
                if (BinaryUtils.isEmpty(ciCodeAndDataMap.get(privateRlt.getTargetCiCode()))) {
                    log.info("##################视图发布，RLT关系发布无法在设计库找到源端CI,源端CI的ciCode：【{}】。当前发布的私有库关系ID：【{}】", privateRlt.getSourceCiCode(), privateRlt.getId());
                    continue;
                }
                if (BinaryUtils.isEmpty(ciCodeAndDataMap.get(privateRlt.getSourceCiCode()))) {
                    log.info("##################视图发布，RLT关系发布无法在设计库找到目标端CI,目标端CI的ciCode：【{}】。当前发布的私有库关系ID：【{}】", privateRlt.getSourceCiCode(), privateRlt.getId());
                    continue;
                }
                BindCiRltRequestDto bindCiRltRequestDto = new BindCiRltRequestDto();
                bindCiRltRequestDto.setTargetCiId(ciCodeAndDataMap.get(privateRlt.getTargetCiCode()).getCi().getId());
                bindCiRltRequestDto.setSourceCiId(ciCodeAndDataMap.get(privateRlt.getSourceCiCode()).getCi().getId());
                bindCiRltRequestDto.setRltClassId(privateRlt.getClassId());
                bindCiRltRequestDto.setAttrs(privateRlt.getAttrs());
                bindCiRltRequestDto.setRepetitionError(false);
                bindCiRltRequestDtos.add(bindCiRltRequestDto);
                // 获取 rlt 的ciCode
                rltCode.add(privateRlt.getCiCode());
                privateRlt.setPublicVersion(BinaryUtils.isEmpty(privateRlt.getPublicVersion()) ? 1 : privateRlt.getPublicVersion() + 1);
                privateRlt.setLocalVersion(1L);
                updateVersionList.add(privateRlt);
            }
        }
        if (!BinaryUtils.isEmpty(updateCiRltAttrMap)) {
            iamsCIRltDesignSvc.updateCiRltAttrBatch(updateCiRltAttrMap);
        }
        // 批量保存关系数据
        if (!CollectionUtils.isEmpty(bindCiRltRequestDtos)) {
            iamsCIRltDesignSvc.bindCiRlts(1L, bindCiRltRequestDtos, false, null, null);
        }

        // 私有库关系数据发布逻辑与CI保持一致 localVersion = 1 publicversion自增+1
        esCIRltPirvateSvc.saveOrUpdateBatch(updateVersionList);

        // 查询出发布后设计库生成的关系数据 当前代码前提需要保证 两个CI 之间的 一种RLT 数据保持唯一
        if (CollectionUtils.isEmpty(rltCode)) {
            return new ArrayList<>();
        }
        ESRltSearchBean esRltSearchBean = new ESRltSearchBean();
        esRltSearchBean.setRltCodes(rltCode);
        esRltSearchBean.setPageSize(rltCode.size());
        esRltSearchBean.setPageNum(1);
        Page<ESCIRltInfo> esciRltInfoPage = iamsCIRltDesignSvc.searchRlt(esRltSearchBean);
        return esciRltInfoPage.getData();
    }

    @Override
    public List<CcCiInfo> dealCheckOutDiagramCI(DiagramPrivateAndDesginData privateAndDesginDataByDEnergyId, String loginCode) {
        /*
        *  1.将设计库数据覆盖到私有库数据上
        *  2.将私有库的数据版本信息同步设计库 publicVersion
        *  3.初始化私有库的本地版本信息 localVersion
        *
        * */
        List<CcCiInfo> desginCiInfos = privateAndDesginDataByDEnergyId.getDesginCiInfos();
        List<CcCiInfo> privateCiInfos = privateAndDesginDataByDEnergyId.getPrivateCiInfos();
        Map<String, CcCiInfo> privateCiCodesAndInfo = new HashMap<>();
        for (CcCiInfo privateInfo : privateCiInfos) {
            privateCiCodesAndInfo.put(privateInfo.getCi().getCiCode(), privateInfo);
        }

        Set<Long> classIds = new HashSet<>();        // classId集合
        List<String> ciCodes = new ArrayList<>();       // 批量保存至私有库的ciCode集合
        List<Long> ciIds = new ArrayList<>();       // 根据ID批量刷新localVersion字段

        List<CcCiInfo> createOrUpdateCIInfos = new ArrayList<>();        // 保存或者更新到私有库的数据
        for (CcCiInfo desginInfo : desginCiInfos) {
            if (!privateCiCodesAndInfo.containsKey(desginInfo.getCi().getCiCode())) {
                // case1: 若当前设计库数据不存在私有库 直接创建
                // 用户私有库不存在设计库数据 在私有库创建 localVersion设为初始数值0 publishVersion / ciCode 与设计库保持对应
                desginInfo.getCi().setLocalVersion(0L);
                desginInfo.getCi().setId(null);
                desginInfo.getCi().setOwnerCode(loginCode);
                createOrUpdateCIInfos.add(desginInfo);
                classIds.add(desginInfo.getCi().getClassId());
                ciCodes.add(desginInfo.getCi().getCiCode());
            } else {
                // case2: 若当前设计库数据存在私有库 以设计库数据为准
                // 将私有库存在的数据与设计库进行版本同一，属性统一 localVerrsion设置为初始值0
                CcCiInfo privateCIInfo = privateCiCodesAndInfo.get(desginInfo.getCi().getCiCode());
                privateCIInfo.getCi().setLocalVersion(0L);
                privateCIInfo.getCi().setOwnerCode(loginCode);
                privateCIInfo.getCi().setPublicVersion(desginInfo.getCi().getPublicVersion());
                privateCIInfo.setAttrs(desginInfo.getAttrs());
                createOrUpdateCIInfos.add(privateCIInfo);
                classIds.add(privateCIInfo.getCi().getClassId());
                ciCodes.add(privateCIInfo.getCi().getCiCode());
                ciIds.add(privateCIInfo.getCi().getId());
            }
        }

        List<CcCiInfo> ciInfos = new ArrayList<>();
        if (CollectionUtils.isEmpty(createOrUpdateCIInfos)) {
            return ciInfos;
        }
        List<ESCIInfo> esciInfos = EamUtil.coverCiInfoList(createOrUpdateCIInfos);
        ciSwitchSvc.saveOrUpdateBatchCI(esciInfos, new ArrayList<>(classIds), loginCode, loginCode, LibType.PRIVATE);      // 批量创建更新至私有库

        // 批量更新私有库CI的方法我发添加loclaVersion字段的操作逻辑 这里直接手动刷新为0
        iamsESCIPrivateSvc.makeZeroCiLocalVersionByIds(ciIds);

        if (!CollectionUtils.isEmpty(ciCodes)) {
            CCcCi cdt = new CCcCi();
            cdt.setCiCodes(ciCodes.toArray(new String[ciCodes.size()]));
            cdt.setOwnerCodeEqual(loginCode);
            // 查询私有库创建更新数据 用于下一步关系创建
            ciInfos = ciSwitchSvc.queryCiInfoList(1L, cdt, null, false, false, LibType.PRIVATE);
        }
        return ciInfos;
    }

    @Override
    public Boolean dealCheckOutDiagramRLT(DiagramPrivateAndDesginData privateAndDesginDataByDEnergyId,
                                          List<CcCiInfo> privateCiInfos, String loginCode) {

        Map<String, CcCiInfo> privateCodeAndDataMap = new HashMap<>();
        for (CcCiInfo ccCiInfo : privateCiInfos) {
            privateCodeAndDataMap.put(ccCiInfo.getCi().getCiCode(), ccCiInfo);
        }
        // 获取视图上的 RLT 数据
        List<ESCIRltInfo> privateRltInfos = privateAndDesginDataByDEnergyId.getPrivateRltInfos();
        List<ESCIRltInfo> desginRltInfos = privateAndDesginDataByDEnergyId.getDesginRltInfos();
        Map<String, ESCIRltInfo> privateRltCodeAndDataMap = new HashMap<>();
        for (ESCIRltInfo privateRlt : privateRltInfos) {
            privateRltCodeAndDataMap.put(privateRlt.getCiCode(), privateRlt);
        }

        Set<BindCiRltRequestDto> bindCiRltRequestDtos = new HashSet<>();
        Map<String, Long> desginRltUniqueCodeAndVersionMap = new HashMap<>();            // 设计库关系数据唯一标识UniqueCode 和 publicversion字段
        for (ESCIRltInfo desginRlt : desginRltInfos) {
            BindCiRltRequestDto bindCiRltRequestDto = new BindCiRltRequestDto();
            if (privateRltCodeAndDataMap.containsKey(desginRlt.getCiCode())) {
                // 更新处理 使用设计库数据覆盖本地私有库
                ESCIRltInfo privateRlt = privateRltCodeAndDataMap.get(desginRlt.getCiCode());
                bindCiRltRequestDto.setTargetCiId(privateRlt.getTargetCiId());
                bindCiRltRequestDto.setSourceCiId(privateRlt.getSourceCiId());
                bindCiRltRequestDto.setRltClassId(privateRlt.getClassId());
                bindCiRltRequestDto.setAttrs(desginRlt.getAttrs());

                // ciRltSwitchSvc.updateCiRltAttr(privateRlt.getId(), desginRlt.getAttrs(), LibType.PRIVATE);
            } else {
                // 新建处理
                bindCiRltRequestDto.setTargetCiId(privateCodeAndDataMap.get(desginRlt.getTargetCiCode()).getCi().getId());
                bindCiRltRequestDto.setSourceCiId(privateCodeAndDataMap.get(desginRlt.getSourceCiCode()).getCi().getId());
                bindCiRltRequestDto.setRltClassId(desginRlt.getClassId());
                bindCiRltRequestDto.setAttrs(desginRlt.getAttrs());
            }
            bindCiRltRequestDto.setRepetitionError(false);
            bindCiRltRequestDto.setOwnerCode(loginCode);
            bindCiRltRequestDtos.add(bindCiRltRequestDto);

            if (!BinaryUtils.isEmpty(desginRlt.getPublicVersion())) {    // 之前设计库存在没有历史版本的关系数据忽略 暂时不考虑版本继承
                desginRltUniqueCodeAndVersionMap.put(desginRlt.getUniqueCode(), desginRlt.getPublicVersion());
            }
        }
        // 批量保存关系数据 重复数据选择覆盖
        if (!CollectionUtils.isEmpty(bindCiRltRequestDtos)) {
            iamsCIRltPrivateSvc.bindCiRlts(1L, bindCiRltRequestDtos, true, null, null);
        }

        // todo 这个逻辑最好添加到上面的保存接口中
        // 刷新私有库关系版本 ：localversion置为1 publicversion设置为设计库对应的关系版本号
        if (!MapUtils.isEmpty(desginRltUniqueCodeAndVersionMap)) {
            this.updatePrivateRltVersion(desginRltUniqueCodeAndVersionMap);
        }
        return true;
    }

    @Override
    public Map<String, List<DiagramChangeData>> checkPrimaryKeyConflict(Map<String, List<ESDiagramDTO>> userCodeAndDiagramInfoMap,
                                                                        Map<String, DiagramPrivateAndDesginData> privateAndDesginDataByUserCode,
                                                                        Boolean isPush) {
        // 目前只能循环用户 检查冲突数据
        Map<String, List<DiagramChangeData>> primaryKeyConflict = new HashMap<>();

        for (String userCode : userCodeAndDiagramInfoMap.keySet()) {
            // 当前用户的主键冲突数据 data
            List<DiagramChangeData> nuptialData = new ArrayList<>();

            DiagramPrivateAndDesginData privateAndDesginDataByDEnergyId = privateAndDesginDataByUserCode.get(userCode);
            List<CcCiInfo> privateCiInfos = privateAndDesginDataByDEnergyId.getPrivateCiInfos();
            List<CcCiInfo> desginCiInfos = privateAndDesginDataByDEnergyId.getDesginCiInfos();

            // 检出的时候 私有库数据可能为空
            if (privateCiInfos.size() == desginCiInfos.size() || (BinaryUtils.isEmpty(privateCiInfos) && isPush)) {
                // 当前用户私有库数据与设计库数据一一对应
                primaryKeyConflict.put(userCode, nuptialData);
                continue;
            }

            // CI code与data对应的map
            Map<String, CcCiInfo> privateCodeAndCiInfoMap = privateCiInfos.stream()
                    .collect(Collectors.toMap(key -> key.getCi().getCiCode(), value -> value, (key1, key2) -> key2));

            Map<String, CcCiInfo> desginCodeAndCiInfoMap = desginCiInfos.stream()
                    .collect(Collectors.toMap(key -> key.getCi().getCiCode(), value -> value, (key1, key2) -> key2));

            // 这里的主键冲突校验规则 发布检出可以共用
            Map<String, CcCiInfo> targetMap = isPush ? desginCodeAndCiInfoMap : privateCodeAndCiInfoMap;
            List<CcCiInfo> sourceCIInfo = isPush ? privateCiInfos : desginCiInfos;

            // 筛选出设计库不存在的数据 primaryKey
            Map<String, CcCiInfo> existTargetAndDataList = new HashMap<>();
            for (CcCiInfo sourceCI : sourceCIInfo) {
                if (!targetMap.containsKey(sourceCI.getCi().getCiCode())) {
                    existTargetAndDataList.put(sourceCI.getCi().getCiPrimaryKey(), sourceCI);
                }
            }

            // 设计库根据名称查询是否存在同主键要素
            List<ESCIInfo> primaryKeyEqualData = new ArrayList<>();

            if (MapUtils.isEmpty(existTargetAndDataList)) {
                // 设计库导入导出可能存在异常数据 排除异常数据影响
                primaryKeyConflict.put(userCode, nuptialData);
                continue;
            }
            List<String> ciPrimaryKeys = new ArrayList<>();
            List<Long> classIds = new ArrayList<>();
            existTargetAndDataList.values().forEach(e -> {
                ciPrimaryKeys.add(e.getCi().getCiPrimaryKey());
                classIds.add(e.getCi().getClassId());
            });
            ESCISearchBean esciSearchBean = new ESCISearchBean();
            esciSearchBean.setCiPrimaryKeys(ciPrimaryKeys);
            esciSearchBean.setClassIds(classIds);
            if (!isPush) {
                esciSearchBean.setOwnerCode(userCode);
            }
            esciSearchBean.setPageNum(1);
            esciSearchBean.setPageSize(ciPrimaryKeys.size());
            Page<ESCIInfo> esciInfoPage = ciSwitchSvc.searchESCIByBean(esciSearchBean, isPush ? LibType.DESIGN : LibType.PRIVATE);
            primaryKeyEqualData.addAll(esciInfoPage.getData());
            if (CollectionUtils.isEmpty(primaryKeyEqualData)) {
                // 设计库无重复主键要素 不需要检出
                primaryKeyConflict.put(userCode, nuptialData);
                continue;
            }
            for (ESCIInfo targetESCIInfo : primaryKeyEqualData) {
                CcCiInfo privateCIInfo = new CcCiInfo();
                CcCiInfo desginCIInfo = new CcCiInfo();

                if (isPush) {
                    privateCIInfo = existTargetAndDataList.get(targetESCIInfo.getCiPrimaryKey());
                    desginCIInfo = EamUtil.tranCcCiInfo(targetESCIInfo, privateCIInfo.getAttrDefs(), privateCIInfo.getCiClass());
                } else {
                    desginCIInfo = existTargetAndDataList.get(targetESCIInfo.getCiPrimaryKey());
                    privateCIInfo = EamUtil.tranCcCiInfo(targetESCIInfo, desginCIInfo.getAttrDefs(), desginCIInfo.getCiClass());
                }

                DiagramChangeData diagramChangeData = new DiagramChangeData(privateCIInfo, desginCIInfo, null, null);
                nuptialData.add(diagramChangeData);
            }

            primaryKeyConflict.put(userCode, nuptialData);
        }
        return primaryKeyConflict;
    }

    @Override
    public Map<String, List<String>> checkCategoryEleNum(Map<Long, List<ESDiagramDTO>> productNumData, Map<String, List<ESDiagramNode>> dEnergyAndNodeMap) {
        // viewType = 0 的数据没有关联制品 直接跳过
        List<Long> productIds = new ArrayList<>();
        for (Long id : productNumData.keySet()) {
            if (!Objects.equals(Long.valueOf(id),Long.valueOf("0"))) {
                productIds.add(id);
            }
        }
        if (CollectionUtils.isEmpty(productIds)) {
            return new HashMap<>();
        }

        // 进入数量校验流程  批量查询出关联制品数据 制品ID 与 制品信息
        Map<Long, List<EamArtifactElementVo>> columnsByList = iEamArtifactColumnSvc.queryAllColumnsByIds(productIds);

        // 制品ID 和 对应的制品约束信息集合
        Map<Long, Set<String>> artCondtionNum = new HashMap<>();
        // 制品ID 和 对应的制品CI约束条件的集合
        Map<Long, Map<String, ArtifactConstraintVo>> artCondtions = new HashMap<>();

        for (Long artId : columnsByList.keySet()) {
            Map<String, ArtifactConstraintVo> elementMap = new HashMap<>();   // 约束信息 与 约束条件
            Set<String> elementList = new HashSet<>();  // 制品图例以及架构元素的约束信息
            List<String> artiElementInfo = new ArrayList<>();  // 每个制品图例以及架构元素的约束信息集合
            for (EamArtifactElementVo eamArtifactElementVo : columnsByList.get(artId)) {
                artiElementInfo.addAll(eamArtifactElementVo.getElements());
            }

            for (String element : artiElementInfo) {
                JSONObject elementJson = JSON.parseObject(element);
                if (!BinaryUtils.isEmpty(elementJson)) {
                    if (!BinaryUtils.isEmpty(elementJson.get("id"))) {
                        String idKey = elementJson.get("id").toString();
                        if (!BinaryUtils.isEmpty(elementJson.get("unique"))) {
                            idKey = idKey + elementJson.get("unique").toString();
                        }
                        elementList.add(idKey);
                        ArtifactConstraintVo constraintVo = new ArtifactConstraintVo();
                        constraintVo.setRelation(elementJson.get("relation").toString());  // 1-等于 2-大于等于 3-小于等于
                        constraintVo.setViewNumber(Integer.valueOf(elementJson.get("viewNumber").toString()));
                        constraintVo.setLabelName(elementJson.get("viewName").toString());
                        elementMap.put(idKey, constraintVo);
                    } else {
                        log.info("=========打印当前异常制品节点信息" + element);
                    }
                }
            }
            artCondtionNum.put(artId, elementList);
            artCondtions.put(artId, elementMap);
        }

        // 若制品无约束条件 直接放开
        if (MapUtils.isEmpty(artCondtionNum)) {
            return new HashMap<>();
        }
        // 批量校验约束数量
        return this.batchCheckCateNumInfo(columnsByList,productNumData, artCondtionNum, artCondtions, dEnergyAndNodeMap);
    }

    /**
     *  批量校验视图必填项数据 支持多用户
     * @param userData 用户ID -》 用户维度的视图
     * @param privateAndDesginDataByUserCode 用户ID -》 用户维度的CI数据
     * @param idAndNodeInfo 视图ID -》 视图上的node信息
     * @return
     */
    @Override
    public Map<String, Map<String, String>> checkRequiredFieldByIds(Map<String, List<ESDiagramDTO>> userData,
                                                                    Map<String, DiagramPrivateAndDesginData> privateAndDesginDataByUserCode,
                                                                    Map<String, List<ESDiagramNode>> idAndNodeInfo) {

        Map<String, Map<String, String>> extCheckByDiagramId = new HashMap<>();      // 返回信息 视图id 对应的必填项缺失map结果集

        for (String userCode : userData.keySet()) {     // 根据用户遍历

            List<CcCiInfo> privateCiInfos = privateAndDesginDataByUserCode.get(userCode).getPrivateCiInfos();       // 获取当前用户所有视图上的CI数据
            List<Long> classIds = privateCiInfos.stream().map(CcCiInfo::getCi).collect(Collectors.toList()).
                    stream().map(CcCi::getClassId).collect(Collectors.toList());
            CCcCiClass cdt = new CCcCiClass();
            cdt.setIds(classIds.toArray(new Long[0]));
            List<CcCiClassInfo> ccCiClassInfos = iciClassApiSvc.queryClassByCdt(cdt);
            Map<Long, CcCiClassInfo> classMap = ccCiClassInfos.stream().collect(Collectors.toMap(each -> each.getCiClass().getId(), each -> each, (k1, k2) -> k2));     // 分类ID 对应的 分类数据

            // 遍历对象结果 进行校验
            Map<String, String> errMap = new HashMap<>();       // 每个CI对应的校验结果
            for (CcCiInfo ci : privateCiInfos) {
                List<CcCiAttrDef> defs = classMap.get(ci.getCi().getClassId()).getAttrDefs();
                Map<String, Integer> errorMsgs = CheckAttrUtil.validateAttrValType(defs, ci.getAttrs());
                if (BinaryUtils.isEmpty(errorMsgs)) {
                    continue;
                }
                errMap.put(ci.getCi().getCiCode(), errorMsgs.keySet().stream().collect(Collectors.joining("|")));
            }

            // 将校验结果组装为 视图id 对应的必填项缺失map结果集
            List<ESDiagramDTO> esDiagramDTOS = userData.get(userCode);
            for (ESDiagramDTO esDiagramDTO : esDiagramDTOS) {
                Map<String, String> newCheckData = new HashMap<>();
                if (!BinaryUtils.isEmpty(idAndNodeInfo.get(esDiagramDTO.getDiagram().getDEnergy()))) {
                    for (ESDiagramNode esDiagramNode : idAndNodeInfo.get(esDiagramDTO.getDiagram().getDEnergy())) {
                        if (errMap.containsKey(esDiagramNode.getCiCode())) {
                            newCheckData.put(esDiagramNode.getCiCode(), errMap.get(esDiagramNode.getCiCode()));
                        }
                    }
                }
                extCheckByDiagramId.put(esDiagramDTO.getDiagram().getDEnergy(), newCheckData);
            }
        }
        return extCheckByDiagramId;
    }

    @Override
    public Map<String, List<DiagramChangeData>> checkCIVersionByIds(Map<String, List<ESDiagramDTO>> userData,
                                                                    Map<String, DiagramPrivateAndDesginData> privateAndDesginDataByUserCode) {

        Map<String, List<DiagramChangeData>> data = new HashMap<>();        // 返回数据 用户对应用户名下版本冲突的CI
        for (String userId : userData.keySet()) {       // 根据用户遍历用户的发布数据
            List<DiagramChangeData> versionChangeData = new ArrayList<>();    // 单用户返回数据

            // 读取视图 CI 数据 判断版本
            List<CcCiInfo> privateCiInfos = privateAndDesginDataByUserCode.get(userId).getPrivateCiInfos();
            List<CcCiInfo> desginCiInfos = privateAndDesginDataByUserCode.get(userId).getDesginCiInfos();

            if (CollectionUtils.isEmpty(desginCiInfos)) {
                // 设计库数据为空 视图数据都为新建  当前用户的视图数据不存在版本冲突
                data.put(userId, versionChangeData);
            } else {
                // 判断私有库与设计库数据是否存在版本冲突
                Map<String, CcCiInfo> desginCodeAndDataInfos = new HashMap<>();
                for (CcCiInfo desginCI : desginCiInfos) {
                    desginCodeAndDataInfos.put(desginCI.getCi().getCiCode(), desginCI);
                }

                for (CcCiInfo privateCI : privateCiInfos) {
                    if (desginCodeAndDataInfos.containsKey(privateCI.getCi().getCiCode())) {
                        // 对比publishVersion
                        CcCiInfo desginCIInfo = desginCodeAndDataInfos.get(privateCI.getCi().getCiCode());
                        if (privateCI.getCi().getPublicVersion() < desginCIInfo.getCi().getPublicVersion()) {
                            // 当前数据存在版本冲突
                            DiagramChangeData diagramChangeData = new DiagramChangeData(privateCI, desginCIInfo, null, null);
                            versionChangeData.add(diagramChangeData);
                        }
                    }
                }
                data.put(userId, versionChangeData);        // 记录着当前用户名下的冲突CI
            }
        }
        return data;
    }

    @Override
    public Boolean freshBindingEleByDEnergyId(List<String> primaryKeys, Integer actionType) {
        SysUser currentUserInfo = SysUtil.getCurrentUserInfo();
        // 获取私有库数据筛选
        Map<String, ESCIInfo> freshCodeMap = new HashMap<>();

        CCcCiClass cdt = new CCcCiClass();
        cdt.setDomainId(1L);
        List<CcCiClassInfo> ccCiClassInfos = iciClassApiSvc.queryClassByCdt(cdt);
        // 获取所有分类ID
        List<Long> classIds = ccCiClassInfos.stream().map(classInfo -> classInfo.getCiClass().getId()).collect(Collectors.toList());

        // 根据业务主键查询 私有库 / 设计库 数据
        ESCISearchBean esciSearchBean = new ESCISearchBean();
        esciSearchBean.setClassIds(classIds);
        esciSearchBean.setCiPrimaryKeys(primaryKeys);
        esciSearchBean.setPageNum(1);
        esciSearchBean.setPageSize(primaryKeys.size());
        Page<ESCIInfo> desginInfoPage = ciSwitchSvc.searchESCIByBean(esciSearchBean, LibType.DESIGN);
        esciSearchBean.setOwnerCode(currentUserInfo.getLoginCode());
        Page<ESCIInfo> privateInfoPage = ciSwitchSvc.searchESCIByBean(esciSearchBean, LibType.PRIVATE);

        // 私有库 / 设计库数据生成map键值对
        Map<String, ESCIInfo> desginInfoMap = desginInfoPage.getData().stream().collect(Collectors.toMap(ESCIInfo::getCiPrimaryKey, v -> v, (v1, v2) -> v2));
        Map<String, ESCIInfo> privateInfoMap = privateInfoPage.getData().stream().collect(Collectors.toMap(ESCIInfo::getCiPrimaryKey, v -> v, (v1, v2) -> v2));


        List<ESCIInfo> updateInfo = new ArrayList<>();
        Set<Long> updateClassIds = new HashSet<>();
        Map<Long, String> replaceMap = new HashMap<>();
        if (actionType == Env.GENERAL_DIAGRAM_PUBLISH) {
            // 发布刷新数据
            List<ESCIInfo> privateESInfo = privateInfoPage.getData();
            for (ESCIInfo privateInfo : privateESInfo) {
                ESCIInfo desginInfo = desginInfoMap.get(privateInfo.getCiPrimaryKey());
                freshCodeMap.put(privateInfo.getCiCode(), desginInfo);
                // 这里需要单刷一下私有库的ciCode
                replaceMap.put(privateInfo.getId(), desginInfo.getCiCode());
//                iamsESCIPrivateSvc.replaceCiCodeById(privateInfo.getId(), desginInfo.getCiCode());
                // 私有库数据强制修改为设计库数据 发布版本修正为设计库版本 本地版本归0
                privateInfo.setAttrs(desginInfo.getAttrs());
                privateInfo.setCiCode(desginInfo.getCiCode());
                privateInfo.setPublicVersion(desginInfo.getPublicVersion());
                privateInfo.setLocalVersion(0L);
                updateClassIds.add(privateInfo.getClassId());
                updateInfo.add(privateInfo);
            }
        } else {
            // 检出刷新数据
            List<ESCIInfo> desginESInfo = desginInfoPage.getData();
            for (ESCIInfo desginInfo : desginESInfo) {
                ESCIInfo privateInfo = privateInfoMap.get(desginInfo.getCiPrimaryKey());
                freshCodeMap.put(privateInfo.getCiCode(), desginInfo);
                replaceMap.put(privateInfo.getId(), desginInfo.getCiCode());
//                iamsESCIPrivateSvc.replaceCiCodeById(privateInfo.getId(), desginInfo.getCiCode());
                // 数据以设计库为准 ID使用私有库 更新私有库对应数据
                desginInfo.setId(privateInfo.getId());
                desginInfo.setLocalVersion(0L);
                desginInfo.setOwnerCode(currentUserInfo.getLoginCode());
                updateClassIds.add(desginInfo.getClassId());
                updateInfo.add(desginInfo);
            }
        }
        iamsESCIPrivateSvc.replaceCiCodeByIds(replaceMap);
        // 批量刷新私有库存在主键冲突的CI
        ciSwitchSvc.saveOrUpdateBatchCI(updateInfo, new ArrayList<>(updateClassIds),
                currentUserInfo.getLoginCode(), currentUserInfo.getLoginCode(), LibType.PRIVATE);

        return processDiagramSvc.freshDiagramNode(freshCodeMap, currentUserInfo.getLoginCode());
    }

    /**
     *  刷新私有库关系数据的 publicVersion 字段 为设计库对应版本号 localVersion = 1
     * @param desginRltUniqueCodeAndVersionMap
     * @return
     */
    private Boolean updatePrivateRltVersion (Map<String, Long> desginRltUniqueCodeAndVersionMap) {
        // 根据设计库的唯一标识在私有库查询对应的关系数据 查询操作为了确认私有库确实存在数据
        ESRltSearchBean esRltSearchBean = new ESRltSearchBean();
        esRltSearchBean.setRltUniqueCodes(desginRltUniqueCodeAndVersionMap.keySet());
        esRltSearchBean.setPageNum(1);
        esRltSearchBean.setOwnerCode(SysUtil.getCurrentUserInfo().getLoginCode());
        esRltSearchBean.setPageSize(desginRltUniqueCodeAndVersionMap.keySet().size());
        Page<ESCIRltInfo> esciRltInfoPage = iamsCIRltPrivateSvc.searchRlt(esRltSearchBean);
        List<ESCIRltInfo> data = esciRltInfoPage.getData();
        if (desginRltUniqueCodeAndVersionMap.keySet().size() != data.size()) {
            log.info("######## 当前使用设计库唯一标志字段在私有库查询 数量不一致 排查 ########");
        }
        if (CollectionUtils.isEmpty(data)) {
            log.info("######## 当前使用设计库唯一标志字段在私有库查询 数量为空 排查 ########");
            return true;
        }
        data.forEach(rlt -> {
            rlt.setPublicVersion(desginRltUniqueCodeAndVersionMap.get(rlt.getUniqueCode()));
            rlt.setLocalVersion(1L);
        });
        esCIRltPirvateSvc.saveOrUpdateBatch(data);
        return true;
    }

    /**
     *  批量校验制品数量 传参新增视图上对应的CI个数信息
     * @param columnsByList 制品id -》 制品信息
     * @param productNumData 制品id -》 视图信息
     * @param artCondtionNum 制品id -》 ci约束（ciCode + uniqueTime）
     * @param artCondtions 制品id -》 （ci约束（ciCode + uniqueTime） -》 具体条件）
     * @param idAndNodeInfo 视图id -》 视图上的node信息
     * @return
     */
    private Map<String, List<String>> batchCheckCateNumInfo(Map<Long, List<EamArtifactElementVo>> columnsByList,
                                                           Map<Long, List<ESDiagramDTO>> productNumData,
                                                           Map<Long, Set<String>> artCondtionNum,
                                                           Map<Long, Map<String, ArtifactConstraintVo>> artCondtions,
                                                           Map<String, List<ESDiagramNode>> idAndNodeInfo) {

        Map<String, List<String>> numMsgList = new HashMap<>();

        // 根据视图加密ID查询视图上的同种要素数量
        Map<String, Map<String, Integer>> numMap = this.queryDiagramEleNums(productNumData, artCondtionNum, idAndNodeInfo);

        for (Long artId : artCondtions.keySet()) {
            for (ESDiagramDTO esDiagramDTO : productNumData.get(artId)) {
                // 这里遍历的视图 下面的约束条件一定是这张视图的
                List<String> msgList = new ArrayList<>();
                for (String ele : artCondtions.get(artId).keySet()) {
                    ArtifactConstraintVo artifactConstraintVo = artCondtions.get(artId).get(ele);
                    String msg = "";
                    if (artifactConstraintVo.getRelation().equals("1")) {
                        // 制品约束条件为 =
                        if (!artifactConstraintVo.getViewNumber().equals(numMap.get(esDiagramDTO.getDiagram().getDEnergy()).get(ele))) {
                            msg = artifactConstraintVo.getLabelName() + "的实例个数必须为" + artifactConstraintVo.getViewNumber();
                        }
                    } else if (artifactConstraintVo.getRelation().equals("2")) {
                        // 制品约束条件为 >=
                        if (numMap.get(esDiagramDTO.getDiagram().getDEnergy()).get(ele) < artifactConstraintVo.getViewNumber()) {
                            msg = artifactConstraintVo.getLabelName() + "的实例个数必须大于或等于" + artifactConstraintVo.getViewNumber();
                        }
                    } else if (artifactConstraintVo.getRelation().equals("3")) {
                        // 制品约束条件为 <=
                        if (numMap.get(esDiagramDTO.getDiagram().getDEnergy()).get(ele) > artifactConstraintVo.getViewNumber()) {
                            msg = artifactConstraintVo.getLabelName() + "的实例个数小于或等于" + artifactConstraintVo.getViewNumber();
                        }
                    }
                    if (!BinaryUtils.isEmpty(msg)) {
                        msgList.add(msg);
                    }
                }
                numMsgList.put(esDiagramDTO.getDiagram().getDEnergy(), msgList);
            }
        }
        return numMsgList;
    }

    /**
     *  批量查询当前视图图内约束CI数量情况
     * @param productNumData 制品ID -》 对应制品的视图集合
     * @param artCondtionNum 制品ID -》 对应的制品约束条件
     * @param idAndNodeInfo 视图id -》 视图上的node信息
     * @return
     */
    private Map<String, Map<String, Integer>> queryDiagramEleNums(Map<Long, List<ESDiagramDTO>> productNumData,
                                                                 Map<Long, Set<String>> artCondtionNum,
                                                                 Map<String, List<ESDiagramNode>> idAndNodeInfo) {

        // 初始化返回数据 将对应的制品的约束的数量设置为0 例：{diagramId：{ciCode + time ：0}}
        Map<String, Map<String, Integer>> artNumMap = new HashMap<>();

        for (Long artId : productNumData.keySet()) {       // 遍历视图信息
            Map<String, Integer> aloneNumMap = new HashMap<>();   // 单图约束数据初始化
            for (ESDiagramDTO esDiagramDTO : productNumData.get(artId)) {
                for (String cond : artCondtionNum.get(artId)) {
                    aloneNumMap.put(cond, 0);
                }
                artNumMap.put(esDiagramDTO.getDiagram().getDEnergy(), aloneNumMap);
            }
        }

        List<Long> ids = new ArrayList<>();
        for (Long artId : productNumData.keySet()) {
            productNumData.get(artId).forEach(e -> {
                ids.add(e.getDiagram().getId());
            });
        }


        Map<String, Long> diagramIdAndArtId = new HashMap<>();        // 在这里获取 视图ID 和 制品ID 对应的map 方便下面获取信息
        // Map<Long, String> idAndEId = new HashMap<>();       // 视图的ID和EID
        for (Long artId : productNumData.keySet()) {
            for (ESDiagramDTO esDiagramDTO : productNumData.get(artId)) {
                diagramIdAndArtId.put(esDiagramDTO.getDiagram().getDEnergy(), artId);
            }
        }
        for (String id : idAndNodeInfo.keySet()) {        // 当前遍历视图ID
            Map<String, Integer> numMap = new HashMap<>();
            for (ESDiagramNode node : idAndNodeInfo.get(id)) {
                String identName = "";
                numMap = artNumMap.get(node.getdEnergy());
                JSONObject nodeJson = JSON.parseObject(node.getNodeJson());
                if (!BinaryUtils.isEmpty(nodeJson)) {
                    if (!BinaryUtils.isEmpty(nodeJson.get("classId")) && !BinaryUtils.isEmpty(nodeJson.get("classId"))) {
                        if (!BinaryUtils.isEmpty(nodeJson.getString("unique"))) {
                            identName = nodeJson.getString("classId") + nodeJson.getString("unique");
                        } else {
                            identName = nodeJson.getString("classId");
                        }
                    } else {
                        if (!BinaryUtils.isEmpty(nodeJson.get("id"))) {
                            identName = nodeJson.getString("id");
                        }
                    }
                }
                if (!BinaryUtils.isEmpty(identName) && artCondtionNum.get(diagramIdAndArtId.get(id)).contains(identName)) {       // 如果图内CI条件不为空 并且 也在对应的制品信息中存在约束 就进行数量记录
                    Integer integer = numMap.get(identName);
                    numMap.put(identName, integer + 1);
                }
            }
            // artNumMap.put(idAndEId.get(id), numMap);        // 视图ID 与 对应的数量
        }
        return artNumMap;
    }
}
